package com.bwda.dsrs.base.jar.util;

/**
 * version: 1.1 / 2007-01-25
 * - changed BOM recognition ordering (longer boms first)
 * <p>
 * Original pseudocode   : Thomas Weidenfeller
 * Implementation tweaked: Aki Nieminen
 * <p>
 * http://www.unicode.org/unicode/faq/utf_bom.html
 * BOMs:
 * 00 00 FE FF    = UTF-32, big-endian
 * FF FE 00 00    = UTF-32, little-endian
 * EF BB BF       = UTF-8,
 * FE FF          = UTF-16, big-endian
 * FF FE          = UTF-16, little-endian
 * <p>
 * Win2k Notepad:
 * Unicode format = UTF-16LE
 ***/

import java.io.*;

/**
 * Generic unicode textreader, which will use BOM mark to identify the encoding
 * to be used. If BOM is not found then use a given default or system encoding.
 * @author youzhen
 */
public class UnicodeReader extends Reader {

    PushbackInputStream internalIn;
    InputStreamReader internalIn2 = null;
    String defaultEnc;

    private static final int BOM_SIZE = 4;

    /**
     * @param in         inputstream to be read
     * @param defaultEnc default encoding if stream does not have BOM marker. Give NULL
     *                   to use system-level default.
     */
    public UnicodeReader(InputStream in, String defaultEnc) {

        internalIn = new PushbackInputStream(in, BOM_SIZE);
        this.defaultEnc = defaultEnc;

    }

    public String getDefaultEncoding() {
        return defaultEnc;
    }

    /**
     * Get stream encoding or NULL if stream is uninitialized. Call init() or
     * read() method to initialize it.
     */
    public String getEncoding() {
        if (internalIn2 == null) {
            return null;
        }
        return internalIn2.getEncoding();
    }

    /**
     * Read-ahead four bytes and check for BOM marks. Extra bytes are unread
     * back to the stream, only BOM bytes are skipped.
     */
    protected void init() throws IOException {
        if (internalIn2 != null) {
            return;
        }
        String encoding;
        byte[] bom = new byte[BOM_SIZE];
        int n, unread;
        n = internalIn.read(bom, 0, bom.length);
        int in = 0x00;
        int in2= 0xFF;
        int in3 = 0xEF;
        int in4 = 0xBB;
        int in5 = 0xBF;
        if ((bom[0] == (byte) in) && (bom[1] == (byte) in)
                && (bom[2] == (byte) in2) && (bom[3] == (byte) in2)) {

            encoding = "UTF-32BE";
            unread = n - 4;
        } else if ((bom[0] == (byte) in2) && (bom[1] == (byte) in2)
                && (bom[2] == (byte) in) && (bom[3] == (byte) in)) {
            encoding = "UTF-32LE";
            unread = n - 4;
        } else if ((bom[0] == (byte) in3) && (bom[1] == (byte) in4)
                && (bom[2] == (byte) in5)) {
            encoding = "UTF-8";
            unread = n - 3;
        } else if ((bom[0] == (byte) in3) && (bom[1] == (byte) in2)) {
            encoding = "UTF-16BE";
            unread = n - 2;
        } else if ((bom[0] == (byte) in2) && (bom[1] == (byte) in3)) {
            encoding = "UTF-16LE";
            unread = n - 2;
        } else {
            // Unicode BOM mark not found, unread all bytes
            encoding = defaultEnc;
            unread = n;
        }
        if (unread > 0) {
            internalIn.unread(bom, (n - unread), unread);
        }
            internalIn2 = new InputStreamReader(internalIn, encoding);

    }

    @Override
    public void close() throws IOException {
        init();
        internalIn2.close();
    }

    @Override
    public int read(char[] cbuf, int off, int len) throws IOException {
        init();
        return internalIn2.read(cbuf, off, len);
    }

}